$MOD186
$EP
NAME TIMER
; Main program for uPD70208 microcomputer system
;
; Author: 	Dr Tay Teng Tiow
; Address:     	Department of Electrical Engineering 
;         	National University of Singapore
;		10, Kent Ridge Crescent
;		Singapore 0511.	
; Date:   	6th September 1991
;
; This file contains proprietory information and cannot be copied 
; or distributed without prior permission from the author.
; =========================================================================

public	serial_rec_action, timer2_action, timer1_action
extrn	print_char:far, print_2hex:far, iodefine:far
extrn   set_timers:far

STACK_SEG	SEGMENT
		DB	256 DUP(?)
	TOS	LABEL	WORD
STACK_SEG	ENDS


DATA_SEG	SEGMENT
	TIMER0_MESS	DB	10,13,'TIMER2 INTERRUPT    '
	T_COUNT		DB	2FH
	T_COUNT_SET	DB	2FH
	REC_MESS	DB	10,13,'Period of timer0 =     '
	
	DEBUG_START DB ''

;LCD Stored Data
	LCD_Line1	DB	'Welcome.........'
	LCD_Line2	DB  'Price Tag No. 01'

	LCD_PTR1  DB 0
	LCD_PTR2  DB 0

	LCD_BUFFER_READY DB 0	

;Header
	HEADER_MATCH DB 0 ;count number of byte match
	HEADER_IDX DB 0

	HEADER db '0','0','0','0','1'

DATA_SEG	ENDS

CODE_SEG	SEGMENT
PUBLIC		START
	PPI_8255_PORTA EQU 080H ;port a
	PPI_8255_PORTB EQU 081H ;port b
	PPI_8255_PORTC EQU 082H ;port c
	PPI_8255_CWR EQU 083H ;command word register

	END_BYTE EQU 'z'
	HEADER_LEN EQU 5
ASSUME	CS:CODE_SEG, SS:STACK_SEG, DS:DATA_SEG

START:
;initialize stack area
	MOV	AX,STACK_SEG		
	MOV	SS,AX
	MOV	SP,TOS

; Initialize the on-chip pheripherals
	CALL	FAR PTR	IODEFINE

; ^^^^^^^^^^^^^^^^^  Start of User Main Routine  ^^^^^^^^^^^^^^^^^^
	MOV AX, DATA_SEG
	MOV DS, AX
	
	call far ptr INITIALIZE_LCD
	call far ptr WRITE_LCD_LINE1
	call far ptr WRITE_LCD_LINE2

	MOV DS:LCD_PTR1, 0
	MOV DS:LCD_PTR2, 0
	MOV DS:LCD_BUFFER_READY, 0
    mov DS:header_match,0
    mov ds:HEADER_IDX,0


    STI
NEXT:     
	CMP DS:LCD_BUFFER_READY,0
	je NEXT
;if LCD_buffer is ready
	call far ptr WRITE_LCD_LINE1
	call far ptr WRITE_LCD_LINE2	
;	CALL FAR PTR PRINT_LINE1_DEBUG
;	CALL FAR PTR PRINT_LINE2_DEBUG
;	CALL FAR PTR PRINTLN_DEBUG

	MOV DS:LCD_PTR1,0
	MOV DS:LCD_PTR2,0
	MOV DS:LCD_BUFFER_READY,0
	
	JMP NEXT

; ^^^^^^^^^^^^^^^ End of User main routine ^^^^^^^^^^^^^^^^^^^^^^^^^
SERIAL_REC_ACTION	PROC	FAR
	PUSH 	BX
	PUSH	DS

	MOV	BX,DATA_SEG		;initialize data segment register
	MOV	DS,BX

;	mov BL,AL
;	call far ptr print_char
;	mov al,Bl
	

;if(headerID != header_len) -- still in header 
;	check header -> skip
	CMP DS:HEADER_IDX,HEADER_LEN
	JAE CONTENT_CHECK
	
	CALL HEADER_CHECK
;	mov AL,DS:HEADER_IDX
;	call far ptr print_2hex
	JMP serial_action_end
;else -- not in header
CONTENT_CHECK:
;	if(HEADER_MATCH == HEADER_LEN)
;		receive until end byte
;	else -- discard message if not recipient 
;		wait until end byte to reset
	CMP DS:HEADER_MATCH,HEADER_LEN
	jne DISCARD_MESSAGE

	call PROCESS_CONTENT

	JMP serial_action_end

DISCARD_MESSAGE:
	CMP AL,END_BYTE
	jne serial_action_end
	mov ds:HEADER_MATCH,0
	mov ds:HEADER_IDX,0

serial_action_end:
	POP	DS
	POP	BX
	RET
SERIAL_REC_ACTION	ENDP

;------------------------------------------
PROCESS_CONTENT proc near
	CMP AL,END_BYTE
	je FINISH_RECEIVE
	call far ptr WRITE_LCD_BUFFER

	JMP FINISH_PROCESS_CONTENT

FINISH_RECEIVE:
	inc DS:LCD_BUFFER_READY
;clear header info
	mov ds:HEADER_MATCH,0
	mov ds:HEADER_IDX,0
FINISH_PROCESS_CONTENT:
	RET
PROCESS_CONTENT ENDP
;------------------------------------------

HEADER_CHECK proc near
	push BX

	XOR BX, BX
	MOV bl, DS:HEADER_IDX
 
;if (!header_match)
;	skip
;else
;	increase header_match counter

	CMP AL, DS:HEADER[BX]
	JNE END_HEADER_CHECK

	INC DS:HEADER_MATCH
	
;	mov al,DS:header_match
;	call far ptr print_2hex
END_HEADER_CHECK:	

	INC DS:HEADER_IDX ;check the next header byte

	pop BX
	ret
HEADER_CHECK endp
;------------------------------------------
WRITE_LCD_BUFFER proc far
	PUSH BX

	XOR BX,BX
	cmp DS:LCD_PTR1,16
	jae LINE_2

;LINE_1:
	MOV BL,DS:LCD_PTR1
	mov DS:LCD_Line1[BX],AL
	INC DS:LCD_PTR1

	JMP FINISH_WRITE_LCD_BUFFER
LINE_2:
	MOV BL,DS:LCD_PTR2
	mov DS:LCD_Line2[BX],AL
	INC DS:LCD_PTR2

FINISH_WRITE_LCD_BUFFER:
	pop BX
	ret
WRITE_LCD_BUFFER endp


timer1_action proc far
	ret
timer1_action endp


timer2_action proc far
	ret
timer2_action endp
;============================================================

INITIALIZE_LCD PROC FAR
	push AX
	push DX
;-------------------------------------------
;Initialize Ports
	MOV AL, 080H; ALL OUTPUT
	MOV DX, PPI_8255_CWR
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	call far ptr delay_100us
;-------------------------------------------
;	;FUNCTION SET FORMAT
;	
;	;DL=H: 8 bit, else 4 bit.    
;	;N=L: 1 line, N=H: 2 lines
;	;F=L:  5x8  , F=H: 5x11
;	
;	;00 001(DL) (N)(F)--
;	;00 001 1    1  1 00
;	;PORTB(7:0) = DB(7:0)
;	;PORTC: XX(E)(R/W) (RS)XXX
;-------------------------------------------
;Function set 1
	MOV DX, PPI_8255_PORTC
	MOV AL, 00100000b ;enable (1), R/W low (0), RS low (0)
	OUT DX, AL

	MOV DX, PPI_8255_PORTB ;0011 XXXX
	MOV AL, 00111100B
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	mov al,5
	call far ptr delay_ms
;-------------------------------------------
;Function set 2
	MOV DX, PPI_8255_PORTC
	MOV AL, 00100000b ;enable (1), R/W low (0), RS low (0)
	OUT DX, AL

	MOV DX, PPI_8255_PORTB ;0011 XXXX
	MOV AL, 00111100B
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL
	
	call far ptr delay_100us
;-------------------------------------------
;Function set 3
	MOV DX, PPI_8255_PORTC
	MOV AL, 00100000b ;enable (1), R/W low (0), RS low (0)
	OUT DX, AL

	MOV DX, PPI_8255_PORTB ;0011 XXXX
	MOV AL, 00111100B
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	call far ptr delay_100us
;-------------------------------------------
;Function Set 4
	MOV DX, PPI_8255_PORTC ;E(1), RW(0), RS(0)
	MOV AL, 00100000b
	OUT DX, AL

	MOV DX, PPI_8255_PORTB ;0011 NFXX 
	MOV AL, 00111100B
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	call far ptr delay_100us

;-------------------------------------------
;	;Display ON/OFF Control FORMAT
;	;(RS)(RW) 0000 1(D)(C)(B)
;	;(RS)(RW) = 00
;	;Display(D) On/off control bit
;	;Cursor(C) On/off control bit 
;	;Cursor Blink(B) On/off control bit
;-------------------------------------------
;Display off - required 38us
;(RS)(RW) 0000 1(D)(C)(B)
; 0   0   0000 1 0  0  0

	MOV DX, PPI_8255_PORTB ;0000 1000 
	MOV AL, 00001000b
	OUT DX, AL

	MOV DX, PPI_8255_PORTC ;enable (1), R/W low (0), RS low (0)
	MOV AL, 00100000b
	OUT DX, AL

	MOV DX, PPI_8255_PORTC
	MOV AL, 0
	OUT DX, AL

	call far ptr delay_100us
;-------------------------------------------
;Clear Display - required 1.52ms
;00 0000 0001
	MOV DX, PPI_8255_PORTB ;0000 0001
	MOV AL, 00000001B
	OUT DX, AL

	MOV DX, PPI_8255_PORTC ;enable (1), R/W low (0), RS low (0)
	MOV AL, 00100000b
	OUT DX, AL

	MOV DX, PPI_8255_PORTC
	MOV AL, 0
	OUT DX, AL

	mov AL,2
	call far ptr delay_ms
;-------------------------------------------
;	;Entry mode set FORMAT
;	;00 0000 0 1 ID SH
;	;I/D=H, cursor/blink move right, DDRAM add +1
;	;I/D=L, cursor/blink move left, DDRAM add -1
;	;Shift S=L: shifting of entire display is not performed
;	;Shift S=H: shifting of entire display is performed according to ID value
;	;I/D=H --> shift left, L --> shift right
;-------------------------------------------
;Entry mode set - required 38us
;00 0000 01(ID)(SH)
;00 0000 01 1   0

	MOV DX, PPI_8255_PORTB ;0000 01 I/D S
	MOV AL, 00000110b
	OUT DX, AL

	MOV DX, PPI_8255_PORTC ;enable (1), R/W low (0), RS low (0)
	MOV AL, 00100000b
	OUT DX, AL

	MOV DX, PPI_8255_PORTC ;enable (1), R/W low (0), RS low (0)
	MOV AL, 0
	OUT DX, AL

	call far ptr delay_100us		
;-------------------------------------------
;	;Display ON/OFF Control
;	;(RS)(RW) 0000 1(D)(C)(B)
;	;(RS)(RW) = 00
;	;Display(D) On/off control bit
;	;Cursor(C) On/off control bit 
;	;Cursor Blink(B) On/off control bit
;-------------------------------------------
;Display On: - required 38us
;(RS)(RW) 0000 1(D)(C)(B)
; 0   0   0000 1 1  0  0  (D=1: display On)
	MOV DX, PPI_8255_PORTC ;enable (1), R/W low (0), RS low (0)
	MOV AL, 00100000b
	OUT DX, AL

	MOV AL, 00001100B
	MOV DX, PPI_8255_PORTB
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL
	call far ptr delay_100us
;-------------------------------------------
	pop DX
	pop AX
	RET
INITIALIZE_LCD ENDP	
;===========================================================

WRITE_LCD_LINE1 proc far
	push AX
	push DX

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL
;-------------------------------------------
;	;Set DDRAM address FORMAT
;		;RS RW DB7 DB6:0
;		;1  0  1   address:
;	;For 2 lines mode:
;	;1st row: 00h -> 27h
;	;2nd row: 40h -> 67h
;-------------------------------------------
;H set DDRAM addr
	MOV AL, 10000000B ; 00H
	MOV DX, PPI_8255_PORTB
	OUT DX, AL

	MOV AL, 00100000B
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	call far ptr delay_100us
;-------------------------------------------
;loop to display the content in the data segment
	XOR CX, CX
	XOR BX, BX
	MOV CL, 16
	MOV BL, 0
LOOP_WRITE_LCD_LINE1:
;-------------------------------------------
;	;Write data to ram FORMAT
;	;RS RW DB7:0
;	;1  0  data
;-------------------------------------------
;J Write Data to Ram
	MOV AL, DS:LCD_Line1[BX]
	MOV DX, PPI_8255_PORTB
	OUT DX, AL

	MOV AL,00101000B ;E(1),RW(0),RS(1)
	MOV DX, PPI_8255_PORTC
	OUT DX,AL

	mov AL,0
	mov DX, PPI_8255_PORTC
	out DX,AL
;-------------------------------------------
;	;Read data from RAM
;	;RS RW DB7:0
;	;1  1  
;-------------------------------------------
;Read Data from Ram
	MOV AL, 00111000B ;E(1),RW(1),RS(1)
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	mov AL,0
	mov DX, PPI_8255_PORTC
	out DX,AL
;Need time to display!
	call far ptr delay_100us

	INC BX
	LOOP LOOP_WRITE_LCD_LINE1

	pop DX
	pop AX
	ret
WRITE_LCD_LINE1 endp

;----------------------------------------------------------

WRITE_LCD_LINE2 proc far
	push AX
	push DX

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL
;-------------------------------------------
;	;Set DDRAM address FORMAT
;		;RS RW DB7 DB6:0
;		;1  0  1   address:
;	;For 2 lines mode:
;	;1st row: 00h -> 27h
;	;2nd row: 40h -> 67h
;-------------------------------------------
;H set DDRAM addr
	MOV AL, 11000000B ; 40H
	MOV DX, PPI_8255_PORTB
	OUT DX, AL

	MOV AL, 00100000B
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	MOV AL, 0
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	call far ptr delay_100us
;-------------------------------------------
;loop to display the content in the data segment
	XOR CX, CX
	XOR BX, BX
	MOV CL, 16
	MOV BL, 0
LOOP_WRITE_LCD_LINE2:
;-------------------------------------------
;	;Write data to ram FORMAT
;	;RS RW DB7:0
;	;1  0  data
;-------------------------------------------
;J Write Data to Ram
	MOV AL, DS:LCD_Line2[BX]
	MOV DX, PPI_8255_PORTB
	OUT DX, AL

	MOV AL,00101000B ;E(1),RW(0),RS(1)
	MOV DX, PPI_8255_PORTC
	OUT DX,AL

	mov AL,0
	mov DX, PPI_8255_PORTC
	out DX,AL
;-------------------------------------------
;	;Read data from RAM
;	;RS RW DB7:0
;	;1  1  
;-------------------------------------------
;Read Data from Ram
	MOV AL, 00111000B ;E(1),RW(1),RS(1)
	MOV DX, PPI_8255_PORTC
	OUT DX, AL

	mov AL,0
	mov DX, PPI_8255_PORTC
	out DX,AL
;Need time to display!
	call far ptr delay_100us

	INC BX
	LOOP LOOP_WRITE_LCD_LINE2

	pop DX
	pop AX
	ret
WRITE_LCD_LINE2 endp

;=========================================================
;delay 1s = 8,000,000 clock cycles

;JNZ - 4/13 clock cycles (without/with jump)
;LOOP CX - 6/16 clock cycles (without/with jump)
;NOP  - 3 clock cycles
;MOV immediate - 3/4 clock (8/16bits)
;DEC - 3 clock cycles

;1s    = 8,000,000 cycles
;10ms  = 80,000 cycles
;1ms   = 8,000 cycles
;100us = 800 cycles

delay_ms proc far ;using AX!
	push CX ;14 clock cycles

	mov AH,0 ;3 cycles
	loop_1ms_delay:
		mov CX,8887 ;(80,000-16)/(3+6)=8887
		loop_10ms_delay:
			NOP ;3cycles
		loop loop_10ms_delay ;6/16 cycles
		dec AX ;3 cycles
	JNZ loop_1ms_delay ;4 cycles

	pop CX ;14 clock cycles
	ret
delay_ms endp

delay_100us proc far ;100us = 800 cycles
	push CX ;14 cycles

	mov CX,83 ;(800-28-16-4)/9 = 83
	loop_100ms_delay:
		NOP	;3 cycles
	loop loop_100ms_delay ;6/16

	pop CX ;14 cycles
	ret
delay_100us endp

;==================================================
PRINT_LINE1_DEBUG proc far
	push CX
	push AX
	push DS
	push SI

	MOV	AX,DATA_SEG	;initialize data segment register
	MOV	DS,AX

	XOR AX,AX
	MOV AL,10
	CALL FAR PTR PRINT_CHAR
	MOV AL,13
	CALL FAR PTR PRINT_CHAR

	MOV	CX,16 ;length of the debug msg
	MOV	SI,0 ;use SI as a counter
	PRINT_LINE1_DEBUG_LOOP:
		mov al,ds:LCD_Line1[SI]
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_LINE1_DEBUG_LOOP
	
	pop SI
	pop DS
	pop AX
	pop CX
	RET
PRINT_LINE1_DEBUG endp

PRINT_LINE2_DEBUG proc far
	push CX
	push AX
	push DS
	push SI

	MOV	AX,DATA_SEG	;initialize data segment register
	MOV	DS,AX

	XOR AX,AX
	MOV AL,10
	CALL FAR PTR PRINT_CHAR
	MOV AL,13
	CALL FAR PTR PRINT_CHAR

	MOV	CX,16 ;length of the debug msg
	MOV	SI,0 ;use SI as a counter
	PRINT_LINE2_DEBUG_LOOP:
		mov al,ds:LCD_Line2[SI]
		INC	SI
		CALL FAR PTR PRINT_CHAR
	loop PRINT_LINE2_DEBUG_LOOP
	
	pop SI
	pop DS
	pop AX
	pop CX
	RET
PRINT_LINE2_DEBUG endp


PRINTLN_DEBUG proc far
	push AX

	MOV AL,10
	CALL FAR PTR PRINT_CHAR
	MOV AL,13
	CALL FAR PTR PRINT_CHAR

	pop AX
	ret
PRINTLN_DEBUG endp

CODE_SEG	ENDS
END
